<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>医护端 – 模拟数据导入 & 可视化列表</title>
<link rel="stylesheet"
      href="https://cdnjs.cloudflare.com/ajax/libs/milligram/1.4.1/milligram.min.css">
<style>
  body{background:#f8f9fa;color:#333;font-family:Helvetica,Arial,sans-serif;}
  .container{max-width:1200px;margin:auto;padding:1rem;}
  .card{background:#fff;border-radius:8px;box-shadow:0 2px 6px rgba(0,0,0,.1);
        margin:0.5rem 0;padding:1rem;cursor:pointer;transition:transform .2s;}
  .card:hover{transform:translateY(-2px);}
  .list{max-height:70vh;overflow-y:auto;}
  .chart-modal{position:fixed;top:0;left:0;width:100vw;height:100vh;
               background:rgba(0,0,0,.5);display:none;align-items:center;
               justify-content:center;}
  .chart-box{background:#fff;padding:1rem;border-radius:8px;max-width:90vw;
             max-height:90vh;overflow:auto;}
  .close{position:absolute;top:10px;right:20px;font-size:1.5rem;
         cursor:pointer;color:#666;}
</style>
</head>
<body>

<div class="container">
  <h2>医护端 – 患者训练数据可视化</h2>

  <!-- ① 数据导入区 -->
  <section>
    <button id="genMock" class="button-primary">生成模拟数据</button>
    <input type="file" id="fileInput" accept=".csv,.json" style="margin-left:1rem;">
  </section>

  <hr>

  <!-- ② 列表展示区 -->
  <section class="list" id="patientList"></section>
</div>

<!-- ③ 图表弹窗 -->
<div class="chart-modal" id="chartModal">
  <div class="chart-box">
    <span class="close" id="closeModal">&times;</span>
    <canvas id="detailChart" width="800" height="400"></canvas>
  </div>
</div>

<!-- 第三方库 -->
<script src="https://cdn.jsdelivr.net/npm/faker@5.5.3/dist/faker.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/papaparse@5.4.1/papaparse.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>

<script>
/* ==================== 1. 模拟数据生成 ==================== */
function generateMockData(count = 20) {
  const patients = [];
  for (let i = 0; i < count; i++) {
    const id = faker.datatype.uuid();
    const name = faker.name.firstName() + ' ' + faker.name.lastName();
    // 生成 10 条训练记录（日期、得分、时长、误差）
    const records = [];
    const start = new Date();
    start.setDate(start.getDate() - 30); // 最近 30 天
    for (let d = 0; d < 10; d++) {
      const date = new Date(start);
      date.setDate(date.getDate() + d * 3);
      records.push({
        date: date.toISOString().split('T')[0],
        score: faker.datatype.number({min: 60, max: 100}),
        duration: faker.datatype.float({min: 10, max: 45, precision: 0.1}),
        error: faker.datatype.float({min: 0.5, max: 5, precision: 0.1})
      });
    }
    patients.push({id, name, records});
  }
  return patients;
}

/* ==================== 2. 渲染列表 ==================== */
function renderList(patients) {
  const listEl = document.getElementById('patientList');
  listEl.innerHTML = '';
  patients.forEach(p => {
    const card = document.createElement('div');
    card.className = 'card';
    card.dataset.id = p.id;
    card.innerHTML = `
      <strong>${p.name}</strong><br>
      最近一次训练得分：${p.records[p.records.length-1].score}
    `;
    card.onclick = () => showChart(p);
    listEl.appendChild(card);
  });
}

/* ==================== 3. 折线图弹窗 ==================== */
let chartInstance = null;
function showChart(patient) {
  const ctx = document.getElementById('detailChart').getContext('2d');
  const labels = patient.records.map(r => r.date);
  const scores = patient.records.map(r => r.score);
  const durations = patient.records.map(r => r.duration);
  const errors = patient.records.map(r => r.error);

  const data = {
    labels,
    datasets: [
      {label:'得分', data:scores, borderColor:'#2f54eb', fill:false},
      {label:'时长(min)', data:durations, borderColor:'#13c2c2', fill:false},
      {label:'误差(mm)', data:errors, borderColor:'#fa8c16', fill:false}
    ]
  };
  const config = {type:'line', data, options:{responsive:true}};

  if (chartInstance) chartInstance.destroy();
  chartInstance = new Chart(ctx, config);
  document.getElementById('chartModal').style.display = 'flex';
}

/* 关闭弹窗 */
document.getElementById('closeModal').onclick = () => {
  document.getElementById('chartModal').style.display = 'none';
};

/* ==================== 4. 文件导入 ==================== */
document.getElementById('fileInput').addEventListener('change', e => {
  const file = e.target.files[0];
  if (!file) return;
  const ext = file.name.split('.').pop().toLowerCase();

  if (ext === 'json') {
    const reader = new FileReader();
    reader.onload = ev => {
      const data = JSON.parse(ev.target.result);
      renderList(data);
    };
    reader.readAsText(file);
  } else if (ext === 'csv') {
    Papa.parse(file, {
      header: true,
      dynamicTyping: true,
      complete: res => {
        // 假设 CSV 列：patientId,patientName,date,score,duration,error
        const map = {};
        res.data.forEach(row => {
          if (!row.patientId) return;
          if (!map[row.patientId]) {
            map[row.patientId] = {id: row.patientId, name: row.patientName, records: []};
          }
          map[row.patientId].records.push({
            date: row.date,
            score: row.score,
            duration: row.duration,
            error: row.error
          });
        });
        renderList(Object.values(map));
      }
    });
  } else {
    alert('仅支持 .json 或 .csv 文件');
  }
});

/* ==================== 5. 生成并渲染模拟数据 ==================== */
document.getElementById('genMock').onclick = () => {
  const mock = generateMockData(15);
  // 为演示直接在页面渲染，实际项目可把 mock 保存为 JSON 下载
  renderList(mock);
};
</script>
</body>
</html>
